﻿unit Main;

interface

uses
  Windows, Messages, SysUtils, dfHGL;

const
  cDefWindowW = 640;
  cDefWindowH = 480;
  cDefWindowX = 0;
  cDefWindowY = 0;

  cDefConstAtten = 1;
  cDefLinearAtten = 1;
  cDefQuadroAtten = 1;
  NAME_CLASSS = 'TMyWindow';
  WND_TITLE = '';
  FPS_TIMER = 1; // Timer to calculate FPS
  FPS_INTERVAL = 500; // Calculate FPS every 1000 ms

function renderInit(FileName: PAnsiChar): Integer; stdcall;

function renderStep(): Integer; stdcall;

function renderDeInit(): Integer; stdcall;
function renderWindowSetCaption(aCaption: PWideChar): Integer; stdcall;
function renderWindowGetHandle: Integer; stdcall;

var
  WHandle: THandle;
  WC: TWndClass;
  FDC: hDC;
  FHGLRC: hglRC;
  PixelFormat: Integer;
  windowCaption: String;
  FPSCount: Integer = 0; // Counter for FPS

implementation

uses
  Camera, Light, Sprites, Textures, Shaders, Logger, File3ds, VBO,
  dfMath, dfHInput, dfHEngine, glFileTxt,
  Classes;

var
  renderReady: Boolean = False;
  Frames: Integer = 0;
  NewTicks, OldTicks, Freq: Int64;
  dt, FPS: Single;
  cFOV, cZNear, cZFar: Single;

  texID2 { , texID1 } : Integer;

  dx, dy: Integer;
  Scale: Single;
  hHandCursor: HICON;
  backgroundColor: TdfVec3f;
  bDrawAxis: Boolean;

  camPos, camLook, camUp: TdfVec3f;
  // mesh: TdfMesh;
  // vs, fs: Shaders.TShader;
  // prog: Shaders.TShaderProgram;

function MyWindowProc(hWnd: hWnd; Msg: UINT; wParam: wParam;
  lParam: lParam): LRESULT; stdcall;
var
  x, y: Integer;
  d: SmallInt;
begin
  case Msg of
    WM_CLOSE:
      begin
        PostQuitMessage(0);
        Result := 0;
      end;
    WM_TIMER: // Add code here for all timers to be used.
      begin
        if wParam = FPS_TIMER then
        begin
          FPSCount := Round(FPSCount * 1000 / FPS_INTERVAL); // calculate to get per Second incase intercal is less or greater than 1 second
          renderWindowSetCaption
            (Pchar(windowCaption + ' FPS[' + FloatToStr(FPSCount) + ']'));
          Result := 0;
          FPSCount := 0;
        end;
      end;
    WM_PAINT:
      begin
        QueryPerformanceCounter(NewTicks);
        dt := (NewTicks - OldTicks) / Freq;
        OldTicks := NewTicks;
        Inc(Frames);
        FPS := 1 / dt;
        if renderReady then
          renderStep();
        if Frames >= 1000 then
        begin
          renderWindowSetCaption
            (Pchar(windowCaption + ' FPS[' + FloatToStr(FPS) + ']'));
          Frames := 0;
        end;
      end;
    WM_SIZE:
      begin
        Camera.CameraInit(0, 0, LOWORD(lParam), HIWORD(lParam), cFOV, cZNear,
          cZFar);
      end;
    WM_MOUSEMOVE:
      begin
        if wParam and MK_LBUTTON <> 0 then
        begin
          x := LOWORD(lParam);
          y := HIWORD(lParam);
          // Camera.CameraRotate(deg2rad*(x - dx), dfVec3f(0, 1, 0));
          Camera.CameraRotate(deg2rad * (x - dx), CameraGetUp());
          Camera.CameraRotate(deg2rad * (y - dy), CameraGetLeft());
          dx := x;
          dy := y;
        end;
        if wParam and MK_RBUTTON <> 0 then
        begin
          SetCursor(hHandCursor);
          x := LOWORD(lParam);
          y := HIWORD(lParam);
          Camera.CameraPan(y - dy, dx - x);
          dx := x;
          dy := y;
        end;
      end;
    WM_LBUTTONDOWN:
      begin
        dx := LOWORD(lParam);
        dy := HIWORD(lParam);
      end;
    WM_LBUTTONUP:
      begin
        dx := 0;
        dy := 0;
      end;

    WM_RBUTTONDOWN:
      begin
        dx := LOWORD(lParam);
        dy := HIWORD(lParam);
        SetCursor(hHandCursor);
      end;
    WM_RBUTTONUP:
      begin
        dx := 0;
        dy := 0;
      end;

    WM_MOUSEWHEEL:
      begin
        d := HIWORD(wParam);
        dfInput.KeyboardNotifyWheelMoved(d);
      end
    else
      Result := DefWindowProc(hWnd, Msg, wParam, lParam);
  end;
end;

{ --------------------------------------------------------------------- }
{ Properly destroys the window created at startup (no memory leaks) }
{ --------------------------------------------------------------------- }
procedure glKillWnd(Fullscreen: Boolean);
begin
  if Fullscreen then // Change back to non fullscreen
  begin
    ChangeDisplaySettings(devmode(nil^), 0);
    ShowCursor(TRUE);
  end;

  // Makes current rendering context not current, and releases the device
  // context that is used by the rendering context.
  if (not wglMakeCurrent(FDC, 0)) then
    MessageBox(0, 'Release of DC and RC failed!', 'Error',
      MB_OK or MB_ICONERROR);

  // Attempts to delete the rendering context
  if (not wglDeleteContext(FHGLRC)) then
  begin
    MessageBox(0, 'Release of rendering context failed!', 'Error',
      MB_OK or MB_ICONERROR);
    FHGLRC := 0;
  end;

  // Attemps to release the device context
  if ((FDC > 0) and (ReleaseDC(WHandle, FDC) = 0)) then
  begin
    MessageBox(0, 'Release of device context failed!', 'Error',
      MB_OK or MB_ICONERROR);
    FDC := 0;
  end;

  // Attempts to destroy the window
  if ((WHandle <> 0) and (not DestroyWindow(WHandle))) then
  begin
    MessageBox(0, 'Unable to destroy window!', 'Error', MB_OK or MB_ICONERROR);
    WHandle := 0;
  end;

  CloseWindow(WHandle);
  DestroyWindow(WHandle);
end;

{ -------------------------------------------------------------------- }
{ Creates the window and attaches a OpenGL rendering context to it }
{ -------------------------------------------------------------------- }
function glCreateWnd(Width, Height, left, top: Integer; Fullscreen: Boolean;
  PixelDepth: Integer): Boolean;
var
  dwStyle: DWord; // Window styles
  dwExStyle: DWord; // Extended window styles
  dmScreenSettings: devmode; // Screen settings (fullscreen, etc...)
  h_Instance: HINST; // Current instance
  pfd: TPIXELFORMATDESCRIPTOR; // Settings for the OpenGL window
begin
  h_Instance := GetModuleHandle(nil); // Grab An Instance For Our Window
  ZeroMemory(@WC, SizeOf(WC)); // Clear the window class structure

  with WC do // Set up the window class
  begin
    style := CS_HREDRAW or // Redraws entire window if length changes
      CS_VREDRAW or // Redraws entire window if height changes
      CS_OWNDC; // Unique device context for the window
    lpfnWndProc := @MyWindowProc; // Set the window procedure to our func WndProc
    hInstance := h_Instance;
    hCursor := LoadCursor(0, IDC_ARROW);
    lpszClassName := NAME_CLASSS;
  end;

  if (Windows.RegisterClass(WC) = 0) then // Attemp to register the window class
  begin
    MessageBox(0, 'Failed to register the window class!', 'Error',
      MB_OK or MB_ICONERROR);
    Result := False;
    Exit
  end;

  // Change to fullscreen if so desired
  if Fullscreen then
  begin
    ZeroMemory(@dmScreenSettings, SizeOf(dmScreenSettings));
    with dmScreenSettings do
    begin // Set parameters for the screen setting
      dmSize := SizeOf(dmScreenSettings);
      dmPelsWidth := Width; // Window width
      dmPelsHeight := Height; // Window height
      dmBitsPerPel := PixelDepth; // Window color depth
      dmFields := DM_PELSWIDTH or DM_PELSHEIGHT or DM_BITSPERPEL;
    end;

    // Try to change screen mode to fullscreen
    if (ChangeDisplaySettings(dmScreenSettings,
        CDS_FULLSCREEN) = DISP_CHANGE_FAILED) then
    begin
      MessageBox(0, 'Unable to switch to fullscreen!', 'Error',
        MB_OK or MB_ICONERROR);
      Fullscreen := False;
    end;
  end;

  // If we are still in fullscreen then
  if (Fullscreen) then
  begin
    dwStyle := WS_POPUP or // Creates a popup window
      WS_CLIPCHILDREN // Doesn't draw within child windows
      or WS_CLIPSIBLINGS; // Doesn't draw within sibling windows
    dwExStyle := WS_EX_APPWINDOW; // Top level window
    ShowCursor(False); // Turn of the cursor (gets in the way)
  end
  else
  begin
    dwStyle := WS_OVERLAPPEDWINDOW or // Creates an overlapping window
      WS_CLIPCHILDREN or // Doesn't draw within child windows
      WS_CLIPSIBLINGS; // Doesn't draw within sibling windows
    dwExStyle := WS_EX_APPWINDOW or // Top level window
      WS_EX_WINDOWEDGE; // Border with a raised edge
  end;

  // Attempt to create the actual window
  WHandle := CreateWindowEx(dwExStyle, // Extended window styles
    NAME_CLASSS, // Class name
    Pchar(windowCaption), // Window title (caption)
    dwStyle, // Window styles
    left, top, // Window position
    Width, Height, // Size of window
    0, // No parent window
    0, // No menu
    h_Instance, // Instance
    nil); // Pass nothing to WM_CREATE
  if WHandle = 0 then
  begin
    glKillWnd(Fullscreen); // Undo all the settings we've changed
    MessageBox(0, 'Unable to create window!', 'Error', MB_OK or MB_ICONERROR);
    Result := False;
    Exit;
  end;

  // Try to get a device context
  FDC := GetDC(WHandle);
  if (FDC = 0) then
  begin
    glKillWnd(Fullscreen);
    MessageBox(0, 'Unable to get a device context!', 'Error',
      MB_OK or MB_ICONERROR);
    Result := False;
    Exit;
  end;

  // Settings for the OpenGL window
  with pfd do
  begin
    nSize := SizeOf(TPIXELFORMATDESCRIPTOR);
    // Size Of This Pixel Format Descriptor
    nVersion := 1; // The version of this data structure
    dwFlags := PFD_DRAW_TO_WINDOW // Buffer supports drawing to window
      or PFD_SUPPORT_OPENGL // Buffer supports OpenGL drawing
      or PFD_DOUBLEBUFFER; // Supports double buffering
    iPixelType := PFD_TYPE_RGBA; // RGBA color format
    cColorBits := PixelDepth; // OpenGL color depth
    cRedBits := 0; // Number of red bitplanes
    cRedShift := 0; // Shift count for red bitplanes
    cGreenBits := 0; // Number of green bitplanes
    cGreenShift := 0; // Shift count for green bitplanes
    cBlueBits := 0; // Number of blue bitplanes
    cBlueShift := 0; // Shift count for blue bitplanes
    cAlphaBits := 0; // Not supported
    cAlphaShift := 0; // Not supported
    cAccumBits := 0; // No accumulation buffer
    cAccumRedBits := 0; // Number of red bits in a-buffer
    cAccumGreenBits := 0; // Number of green bits in a-buffer
    cAccumBlueBits := 0; // Number of blue bits in a-buffer
    cAccumAlphaBits := 0; // Number of alpha bits in a-buffer
    cDepthBits := 16; // Specifies the depth of the depth buffer
    cStencilBits := 0; // Turn off stencil buffer
    cAuxBuffers := 0; // Not supported
    iLayerType := PFD_MAIN_PLANE; // Ignored
    bReserved := 0; // Number of overlay and underlay planes
    dwLayerMask := 0; // Ignored
    dwVisibleMask := 0; // Transparent color of underlay plane
    dwDamageMask := 0; // Ignored
    { IFDEF DEBUGMEN } logWriteMessage('E0004: Finish Setting OpenGL');
    { ENDIF }
  end;

  // Attempts to find the pixel format supported by a device context that is the best match to a given pixel format specification.
  PixelFormat := ChoosePixelFormat(FDC, @pfd);
  if (PixelFormat = 0) then
  begin
    glKillWnd(Fullscreen);
    MessageBox(0, 'Unable to find a suitable pixel format', 'Error',
      MB_OK or MB_ICONERROR);
    Result := False;
    Exit;
  end;

  // Sets the specified device context's pixel format to the format specified by the PixelFormat.
  if (not SetPixelFormat(FDC, PixelFormat, @pfd)) then
  begin
    glKillWnd(Fullscreen);
    MessageBox(0, 'Unable to set the pixel format', 'Error',
      MB_OK or MB_ICONERROR);
    Result := False;
    Exit;
  end;

  // Create a OpenGL rendering context
  FHGLRC := wglCreateContext(FDC);
  if (FHGLRC = 0) then
  begin
    glKillWnd(Fullscreen);
    MessageBox(0, 'Unable to create an OpenGL rendering context', 'Error',
      MB_OK or MB_ICONERROR);
    Result := False;
    Exit;
  end;

  // Makes the specified OpenGL rendering context the calling thread's current rendering context
  if (not wglMakeCurrent(FDC, FHGLRC)) then
  begin
    glKillWnd(Fullscreen);
    MessageBox(0, 'Unable to activate OpenGL rendering context', 'Error',
      MB_OK or MB_ICONERROR);
    Result := False;
    Exit;
  end;

  SetTimer(WHandle, FPS_TIMER, FPS_INTERVAL, nil);
  // Settings to ensure that the window is the topmost window
  ShowWindow(WHandle, SW_SHOW);
  SetForegroundWindow(WHandle);
  SetFocus(WHandle);

  // Ensure the OpenGL window is resized properly
  // glResizeWnd(Width, Height);
  Result := TRUE;
end;

function renderInit(FileName: PAnsiChar): Integer; stdcall;
  function Checktoken(ch: Char; s: String): Bool;
  begin
    Result := False;
    if (ch = toEOF) or (s = '$0d$0a') then
      Result := TRUE;
  end;

var
  strData: TFileStream;
  par: TParser;
  lightPos: TdfVec3f;
  lAmb, lDif, lSpec: TdfVec4f;
  lConstAtten, lLinearAtten, lQuadroAtten: Single;
  W, H, x, y: Integer;
  SS: String;

  atomColor: TdfVec3f;
  atomTexturePath: TFileName;
  atomSize: Single;
  dataPath: TFileName;
  Fullscreen: Bool;
begin
  Logger.LogInit();

  W := cDefWindowW;
  H := cDefWindowH;
  x := cDefWindowX;
  y := cDefWindowY;
  lConstAtten := cDefConstAtten;
  lLinearAtten := cDefLinearAtten;
  lQuadroAtten := cDefQuadroAtten;

  try
{$REGION ' ×òåíèå äàííûõ èç ôàéëà êîíôèãà ÷åðåç TParser'}
    if FileExists(FileName) then
      strData := TFileStream.Create(FileName, fmOpenRead)
    else
    begin
      { IFDEF DEBUGMEN }
      logWriteError('E0002: Không tồn tại tệp ''' + FileName + '''', TRUE,
        TRUE, TRUE); { ENDIF }
      Result := -1;
      Exit;
    end;
    par := TParser.Create(strData);
    repeat
      SS := lowercase(par.TokenString);
      if SS = 'decimalseparator' then
      begin
        par.NextToken;
        DecimalSeparator := par.TokenString[1];
      end
      else if SS = 'rendermode' then
      begin
        par.NextToken;
        SS := lowercase(par.TokenString);
        if SS = 'window' then
        begin
          Fullscreen := False;
        end
        else
        begin
          Fullscreen := TRUE;
        end;
      end
      else if SS = 'resolution' then
      begin
        par.NextToken;
        W := par.TokenInt;
        par.NextToken;
        H := par.TokenInt;
      end
      else if SS = 'windowpos' then
      begin
        par.NextToken;
        x := par.TokenInt;
        par.NextToken;
        y := par.TokenInt;
      end
      else if SS = 'backgroundcolor' then
      begin
        par.NextToken;
        backgroundColor.x := par.TokenFloat;
        par.NextToken;
        backgroundColor.y := par.TokenFloat;
        par.NextToken;
        backgroundColor.z := par.TokenFloat;
      end
      else if SS = 'axis' then
      begin
        par.NextToken;
        bDrawAxis := (par.TokenString = 'true');
      end
      else if SS = 'caption' then
      begin
        par.NextToken;
        windowCaption := PWideChar(par.TokenString);
      end
      else if SS = 'fov' then
      begin
        par.NextToken;
        cFOV := par.TokenFloat;
      end
      else if SS = 'znear' then
      begin
        par.NextToken;
        cZNear := par.TokenFloat;
      end
      else if SS = 'zfar' then
      begin
        par.NextToken;
        cZFar := par.TokenFloat;
      end
      else if SS = 'camerapos' then
      begin
        par.NextToken;
        camPos.x := par.TokenFloat;
        par.NextToken;
        camPos.y := par.TokenFloat;
        par.NextToken;
        camPos.z := par.TokenFloat;
      end
      else if SS = 'cameralook' then
      begin
        par.NextToken;
        camLook.x := par.TokenFloat;
        par.NextToken;
        camLook.y := par.TokenFloat;
        par.NextToken;
        camLook.z := par.TokenFloat;
      end
      else if SS = 'cameraup' then
      begin
        par.NextToken;
        camUp.x := par.TokenFloat;
        par.NextToken;
        camUp.y := par.TokenFloat;
        par.NextToken;
        camUp.z := par.TokenFloat;
      end
      else if SS = 'lightpos' then
      begin
        par.NextToken;
        lightPos.x := par.TokenFloat;
        par.NextToken;
        lightPos.y := par.TokenFloat;
        par.NextToken;
        lightPos.z := par.TokenFloat;
      end
      else if SS = 'lightambcolor' then
      begin
        par.NextToken;
        lAmb.x := par.TokenFloat;
        par.NextToken;
        lAmb.y := par.TokenFloat;
        par.NextToken;
        lAmb.z := par.TokenFloat;
        par.NextToken;
        lAmb.W := par.TokenFloat;
      end
      else if SS = 'lightdifcolor' then
      begin
        par.NextToken;
        lDif.x := par.TokenFloat;
        par.NextToken;
        lDif.y := par.TokenFloat;
        par.NextToken;
        lDif.z := par.TokenFloat;
        par.NextToken;
        lDif.W := par.TokenFloat;
      end
      else if SS = 'lightspeccolor' then
      begin
        par.NextToken;
        lSpec.x := par.TokenFloat;
        par.NextToken;
        lSpec.y := par.TokenFloat;
        par.NextToken;
        lSpec.z := par.TokenFloat;
        par.NextToken;
        lSpec.W := par.TokenFloat;
      end
      else if SS = 'lightconstatten' then
      begin
        par.NextToken;
        lConstAtten := par.TokenFloat;
      end
      else if SS = 'lightlinearatten' then
      begin
        par.NextToken;
        lLinearAtten := par.TokenFloat;
      end
      else if SS = 'lightquadroatten' then
      begin
        par.NextToken;
        lQuadroAtten := par.TokenFloat;
      end
      else if SS = 'lightdraw' then
      begin
        par.NextToken;
        Light.bDrawLight := (par.TokenString = 'true');
      end
      else if SS = 'atomcolor' then
      begin
        par.NextToken;
        atomColor.x := par.TokenFloat;
        par.NextToken;
        atomColor.y := par.TokenFloat;
        par.NextToken;
        atomColor.z := par.TokenFloat;
      end
      else if SS = 'atomtexturepath' then
      begin
        par.NextToken;
        atomTexturePath := PWideChar(par.TokenString);
      end
      else if SS = 'atomsize' then
      begin
        par.NextToken;
        atomSize := par.TokenFloat;
      end
      else if SS = 'datapath' then
      begin
        par.NextToken;
        dataPath := PWideChar(par.TokenString);
      end;
    until par.NextToken = toEOF; // Checktoken(par.NextToken, par.TokenString);
    par.Free;
    strData.Free;
    { IFDEF DEBUGMEN }
    logWriteMessage('E0003 Hoàn thành việc load tệp setting' + FileName);
    { ENDIF }
{$ENDREGION}
    glCreateWnd(W, H, x, y, Fullscreen, 32);
    (*
      // Style := WS_OVERLAPPEDWINDOW or WS_CLIPSIBLINGS or WS_CLIPCHILDREN;
      h_Instance := GetModuleHandle(nil);
      ZeroMemory(@WC, SizeOf(TWndClass));
      with WC do
      begin
      Style := CS_VREDRAW or CS_HREDRAW or CS_OWNDC;
      hInstance := h_Instance;
      HICON := LoadIcon(0, IDI_WINLOGO);
      hCursor := LoadCursor(0, IDC_ARROW);
      hDefaultCursor := hCursor;
      hbrBackground := GetStockObject(White_Brush);
      lpfnWndProc := @MyWindowProc;
      lpszClassName := NAME_CLASSS;
      end;
      hHandCursor := LoadCursor(0, IDC_HAND);

      if (Windows.RegisterClass(WC) = 0) then
      // Attemp to register the window class
      begin
      MessageBox(0, 'Failed to register the window class!', 'Error',
      MB_OK or MB_ICONERROR);
      Result := 0;
      Exit
      end;

      // Change to fullscreen if so desired
      if Fullscreen then
      begin
      ZeroMemory(@dmScreenSettings, SizeOf(dmScreenSettings));
      with dmScreenSettings do
      begin // Set parameters for the screen setting
      dmSize := SizeOf(dmScreenSettings);
      dmPelsWidth := W; // Window width
      dmPelsHeight := H; // Window height
      dmBitsPerPel := 16; // Window color depth
      dmFields := DM_PELSWIDTH or DM_PELSHEIGHT or DM_BITSPERPEL;
      end;

      // Try to change screen mode to fullscreen
      if (ChangeDisplaySettings(dmScreenSettings,
      CDS_FULLSCREEN) = DISP_CHANGE_FAILED) then
      begin
      MessageBox(0, 'Unable to switch to fullscreen!', 'Error',
      MB_OK or MB_ICONERROR);
      Fullscreen := False;
      end;
      end;

      // If we are still in fullscreen then
      if (Fullscreen) then
      begin
      dwStyle := WS_POPUP or // Creates a popup window
      WS_CLIPCHILDREN // Doesn't draw within child windows
      or WS_CLIPSIBLINGS; // Doesn't draw within sibling windows
      dwExStyle := WS_EX_APPWINDOW; // Top level window
      ShowCursor(False); // Turn of the cursor (gets in the way)
      end
      else
      begin
      dwStyle := WS_OVERLAPPEDWINDOW or // Creates an overlapping window
      WS_CLIPCHILDREN or // Doesn't draw within child windows
      WS_CLIPSIBLINGS; // Doesn't draw within sibling windows
      dwExStyle := WS_EX_APPWINDOW or // Top level window
      WS_EX_WINDOWEDGE; // Border with a raised edge
      end;

      WHandle := CreateWindowEx(dwExStyle, NAME_CLASSS, PChar(windowCaption), dwStyle, x, y, W,
      H, 0, 0, h_Instance, nil);
      if WHandle = 0 then
      begin
      { IFDEF DEBUGMEN } logWriteError(
      'E0004: Không thể khởi tạo cửa sổ chương trình', true, true, true);
      { ENDIF }
      Result := 1;
      Exit;
      end;
      { IFDEF DEBUGMEN } logWriteMessage(
      'E0005: Khởi tạo cửa sổ chương trình với handle: ' + IntToStr(WHandle));
      { ENDIF }

      FDC := GetDC(WHandle);

      if FDC = 0 then
      begin
      { IFDEF DEBUGMEN }
      logWriteError('E0006: Error on GetDC(WHandle)', true, true, true);
      { ENDIF }
      Result := 2;
      Exit;
      end;

      { IFDEF DEBUGMEN }
      logWriteMessage('E0007: Loadded all Device Context: ' + IntToStr(FDC));
      { ENDIF }

      PFD.nSize := SizeOf(PIXELFORMATDESCRIPTOR);
      PFD.nVersion := 1;
      PFD.dwFlags := PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL or PFD_DOUBLEBUFFER;
      PFD.iPixelType := PFD_TYPE_RGBA;
      PFD.cColorBits := 32;
      PFD.cDepthBits := 16;

      nPixelFormat := ChoosePixelFormat(FDC, @PFD);

      if nPixelFormat = 0 then
      begin
      { IFDEF DEBUGMEN }
      logWriteError('E0008: Độ sâu màu quá nhỏ', true, true, true); { ENDIF }
      Result := 3;
      Exit;
      end;

      { IFDEF DEBUGMEN }
      logWriteMessage('E0009: nPixelFormat PFD: ' + IntToStr(nPixelFormat));
      { ENDIF }

      SetPixelFormat(FDC, nPixelFormat, @PFD);

      FHGLRC := wglCreateContext(FDC);

      if FHGLRC = 0 then
      begin
      { IFDEF DEBUGMEN }
      logWriteError('E0012: Error on wglCreateContext', true, true, true);
      { ENDIF }
      Result := 4;
      Exit;
      end;

      { IFDEF DEBUGMEN }
      logWriteMessage('E0010: wglCreateContext OK: ' + IntToStr(FHGLRC));
      { ENDIF }

      if (not wglMakeCurrent(FDC, FHGLRC)) then
      begin
      { IFDEF DEBUGMEN }
      logWriteMessage('E0020: Unable to activate OpenGL rendering context');
      { ENDIF }
      Result := 5;
      Exit;
      end;
      *)

    gl.Init;
    gl.Enable(GL_DEPTH_TEST);
    gl.Enable(GL_LIGHTING);
    gl.Enable(GL_CULL_FACE);
    // gl.Enable(GL_COLOR_MATERIAL);
    gl.Enable(GL_TEXTURE_2D);
    gl.ClearColor(backgroundColor.x, backgroundColor.y, backgroundColor.z, 1.0);
    gl.SwapInterval(0);

    Camera.CameraInit(0, 0, W, H, cFOV, cZNear, cZFar);
    renderCameraSet(camPos.x, camPos.y, camPos.z, camLook.x, camLook.y,
      camLook.z, camUp.x, camUp.y, camUp.z);
    Light.LightInit();
    renderLightSet(lightPos.x, lightPos.y, lightPos.z, lAmb.x, lAmb.y, lAmb.z,
      lAmb.W, lDif.x, lDif.y, lDif.z, lDif.z, lSpec.x, lSpec.y, lSpec.z,
      lSpec.W, lConstAtten, lLinearAtten, lQuadroAtten);
    Sprites.SpriteInit(atomColor, atomSize);
    // Shaders.ShadersInit();
    // VBO.VBOInit();

    // có thể load từ những file: .BMP, .JPG, .TGA,
    // texID1 := Textures.renderTexLoad('Data\su37.bmp');
    texID2 := renderTexLoad(atomTexturePath);

    if FileTxt = nil then
      FileTxt := TFileTxt.Create;
    FileTxt.LoadMap('map.txt');

    Scale := 1.0;

    QueryPerformanceFrequency(Freq);
    renderReady := TRUE;

    { IFDEF DEBUGMEN } logWriteMessage('E0011: renderTexLoad'); { ENDIF }
    renderSpritesAddFromFile(dataPath);
    // mesh := TdfMesh.Create;
    // mesh.LoadFrom3ds('Data\su37.3ds');
    // VBO.VBOAddDataFromMesh(mesh);

    // vs := TShader.Create(TGLConst.GL_VERTEX_SHADER);
    // vs.LoadFromFile('Data\vs_phong.txt');
    // fs := TShader.Create(TGLConst.GL_FRAGMENT_SHADER);
    // fs.LoadFromFile('Data\fs_phong.txt');
    // prog := TShaderProgram.Create();
    // prog.AttachVertexShader(vs);
    // prog.AttachFragmentShader(fs);
    // prog.Link;
    Result := 0;
  except
    Result := -1;
  end;
end;

function renderStep(): Integer; stdcall;
var
  lp: TdfVec3f;

  procedure DrawTRIANGLES();
  const
    s = 1.0;
    d = 5.0;
  begin
    gl.Beginp(GL_TRIANGLES);
    gl.Vertex3f(-s, 0, d);
    gl.Vertex3f(s, 0, d);
    gl.Vertex3f(0, s, d);
    gl.MatrixMode(GL_PROJECTION);
    gl.Frustum(-0.1, 0.1, -0.1, 0.1, 0.3, 25.0);
    gl.Endp;
  end;

  procedure DrawAxis();
  var
    i: integer;
  begin
    // Draw axes
    gl.Disable(GL_LIGHTING);
    gl.Beginp(GL_LINES);
    gl.Color4ub(255, 0, 0, 255);
    gl.Vertex3f(0, 0, 0);
    gl.Vertex3f(100, 0, 0);

    gl.Color4ub(0, 255, 0, 255);
    gl.Vertex3f(0, 0, 0);
    gl.Vertex3f(0, 100, 0);

    gl.Color4ub(0, 0, 255, 255);
    gl.Vertex3f(0, 0, 0);
    gl.Vertex3f(0, 0, 100);
    gl.Endp();

    gl.Enable(GL_LIGHTING);
    gl.Beginp(GL_POINTS);
    for i := 0 to particles.Count - 1 do
      with PParticle(particles[i])^ do
      begin
        gl.Color4f(Col.x, Col.y, Col.z, 1.0);
        gl.Vertex3f(Pos.x, Pos.y, Pos.z);
      end;
    gl.Endp();
  end;

begin
  Result := 0;
  try
    lp := Light.LightGetPos();
    gl.Clear(GL_COLOR_BUFFER_BIT);
    gl.Clear(GL_DEPTH_BUFFER_BIT);
    gl.MatrixMode(GL_MODELVIEW);
    gl.PushMatrix();
    if Camera.CameraStep(dt) = -1 then
      raise Exception.CreateRes(1);
    if bDrawAxis then
    begin
      DrawAxis();
      DrawTRIANGLES;
      //DrawGLScene;
      // testglcube;
    end;
    // Light.LightStep(dt);
    renderTexBind(texID2);
    FileTxt.glDraw();
    //SpriteStep(dt);
    //renderTexUnbind;
    // Textures.renderTexBind(texID1);
    // prog.Use();
    // prog.SetUniforms('fSpecularPower', 5);
    // prog.SetUniforms('fvLightPosition', Light.LightGetPos());
    // prog.SetUniforms('fvEyePosition', Camera.CameraGetPos());
    // prog.SetUniforms('fvAmbient', dfVec4f(0.02, 0.02, 0.02, 1.0));
    // prog.SetUniforms('fvDiffuse', dfVec4f(0.68, 0.68, 0.68, 1.0));
    // prog.SetUniforms('fvSpecular', dfVec4f(0.2, 0.2, 0.2, 1.0));
    // prog.SetUniforms('baseMap', 0);
    /// /      gl.PolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    // VBO.VBOStep(dt);
    /// /      gl.PolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    // prog.UseNull;
    // Textures.renderTexUnbind;
    if dfInput.IsKeyDown(VK_MOUSEWHEELUP) then
      CameraScale(-5.0)
    else if dfInput.IsKeyDown(VK_MOUSEWHEELDOWN) then
      CameraScale(5.0);

    if dfInput.IsKeyDown('z') or dfInput.IsKeyDown('ÿ') then
      CameraRotate(0.001, CameraGetDir())
    else if dfInput.IsKeyDown('x') or dfInput.IsKeyDown('÷') then
      CameraRotate(-0.001, CameraGetDir());

    if dfInput.IsKeyDown('c') or dfInput.IsKeyDown('ñ') then
      renderCameraSet(camPos.x, camPos.y, camPos.z, camLook.x, camLook.y,
        camLook.z, camUp.x, camUp.y, camUp.z);

    gl.PopMatrix();
    Windows.SwapBuffers(FDC);
  except
    on E: Exception do
      case StrToInt(E.Message) of
        1:
          Result := 1;
        2:
          Result := 2;
      else
        Result := -1;
      end;
  end;
end;

function renderDeInit(): Integer; stdcall;
begin
  Result := 0;
  { IFDEF DEBUGMEN } logWriteMessage('E0001: Chuẩn bị thoát'); { ENDIF }
  try
    renderReady := False;
    Camera.CameraDeInit();
    Light.LightDeInit();
    Sprites.SpriteDeInit();
    Shaders.ShadersDeinit();
    Textures.TexDeInit();
    Textures.renderTexDel(texID2);
    // mesh.Free;
    // prog.Free;
    //VBO.VBODeInit();
    wglDeleteContext(FHGLRC);
    ReleaseDC(WHandle, FDC);
    wglMakeCurrent(FDC, 0);
    FDC := 0;
    FHGLRC := 0;
    CloseWindow(WHandle);
    DestroyWindow(WHandle);
    WHandle := 0;
    Logger.LogDeinit();
  except
    Result := -1;
    Exit;
  end;
  // *
end;

function renderWindowSetCaption(aCaption: PWideChar): Integer; stdcall;
begin
  SetWindowText(WHandle, aCaption);
  Result := 0;
end;

function renderWindowGetHandle: Integer; stdcall;
begin
  Result := WHandle;
end;

end.
